package com.apobates.forum.toro.template.strategy;

import com.apobates.forum.toro.template.TemplateAffixStatements;
import com.apobates.forum.toro.utils.BeanHelper;
import com.apobates.forum.toro.utils.CommonHelper;
import org.apache.commons.lang3.tuple.Triple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

public abstract class AbstractTemplateAffixStrategy<E> {
    private final static Logger logger = LoggerFactory.getLogger(AbstractTemplateAffixStrategy.class);

    /**
     * 标识子类的名称
     * @return
     */
    protected abstract String getSentinel();
    // 助手工具方法
    /**
     * 获取Class中以指定前缀打头的属性名称
     * @param classIns 目标对象的Class
     * @param affixChars 指定的属性前缀
     * @return
     */
    protected Set<String> getAffixAttrSet(Class<E> classIns, final String affixChars){
        return BeanHelper.getClassField(classIns).stream().filter(attr->attr.startsWith(affixChars)).collect(Collectors.toSet());
    }

    /**
     * 设置对象的指定属性值集合
     * @param targetObj 设置的目标对象
     * @param setAttrVals 设置的属性名和值集俣
     * @param <E>
     */
    protected <E> void setTargetMultiAttrVal(E targetObj, Map<String,Object> setAttrVals) {
        // 5)批量设置Member下的相关属性
        try {
            BeanHelper.setMultiAttrVal(targetObj, setAttrVals);
        }catch (Exception e){
            logger.debug(String.format(
                    "[Template-Affix][%s]设置%s的%s属性失败. 汲及属集数量",
                    getSentinel(),
                    targetObj.getClass().getSimpleName(),
                    String.join(",", setAttrVals.keySet()),
                    setAttrVals.size()));
        }
    }

    /**
     * 获取指定关联对象的中属性名称和值
     * @param funArgVal 关联函数的参数.用它可以取得关联对象
     * @param fun 关联函数
     * @param affixChar 属性的前缀,例:aut
     * @param setAffixAttrs 需要设置的属性名称集合
     * @return key = 需要设置的属性名称,也是setAffixAttrs的元素(包含affix)，value = 设置的属性值
     */
    protected Map<String,Object> getGroupObjAttrVal(
            Object funArgVal,
            Function<String, ?> fun,
            String affixChar,
            Set<String> setAffixAttrs) {
        // 1)计算Member实例
        Object tsObjIns = fun.apply(funArgVal.toString());
        // 2)获取Member中需要聚合的属性及值
        Map<String,Object> tsObjAttrValMap = new HashMap<>();
        try {
            for(String affixAttrName : setAffixAttrs) {
                String getAttrName = affixAttrName.replace(affixChar, "");
                Object tmp = BeanHelper.getAttrVal(tsObjIns, CommonHelper.lowerFirstCase(getAttrName));
                tsObjAttrValMap.put(affixAttrName, tmp);
            }
        }catch (Exception e){
            logger.debug(String.format("[Template-Affix][%s]获取待聚合的属性名且值集合", getSentinel()));
        }
        return tsObjAttrValMap;
    }
    // 从候选集合中搜索期待的元素
    protected Optional<?> seekCollectionEle(Object expectVal, String expectAttrName, Collection<?> result){
        if(null == result || result.isEmpty()){
            return Optional.empty();
        }
        return CommonHelper.seekFirstEle(result, expectAttrName, expectVal);
    }
    // 从指定对象中收集属性值, key = 表示目标对象的属性名(有前缀)/不是收集对象的属性名(无前缀)
    protected Map<String,Object> getGroupObjAttrVal(
            Object relateObj,
            String affixChar,
            Set<String> setAffixAttrs) {
        // 2)获取Member中需要聚合的属性及值
        Map<String,Object> tsObjAttrValMap = new HashMap<>();
        try {
            for(String affixAttrName : setAffixAttrs) {
                String getAttrName = affixAttrName.replace(affixChar, "");
                Object tmp = BeanHelper.getAttrVal(relateObj, CommonHelper.lowerFirstCase(getAttrName));
                tsObjAttrValMap.put(affixAttrName, tmp);
            }
        }catch (Exception e){
            logger.debug(String.format("[Template-Affix][%s]获取待聚合的属性名且值集合", getSentinel()));
        }
        return tsObjAttrValMap;
    }

    /**
     * 一对一中间操作
     * @param tas 前缀语句
     * @param targetObjClass 目标对象的Class
     * @param targetObj 目标对象实例
     * @param selectObj 起始对象实例
     * @param queryFun 绑定对象查询函数
     */
    protected void bindBasisMultiAttr(TemplateAffixStatements tas, Class<E> targetObjClass, Object targetObj, Object selectObj, Function<String, ?> queryFun){
        // 查找以affix开头的属性
        final String affix = tas.getAffix();
        // 3: 当前要设置的属性集合
        Set<String> setAffixAttrs = getAffixAttrSet(targetObjClass, affix);
        // 4: 当前需要设置的属性名和值Map
        Object queryArgVal = BeanHelper.getAttrVal(selectObj, tas.getJoinAttr());
        Map<String,Object> setAttrVals = getGroupObjAttrVal(
                queryArgVal,
                queryFun,
                affix,
                setAffixAttrs);
        // 5: 设置对象的指定属性值集合
        setTargetMultiAttrVal(targetObj, setAttrVals);
    }

    /**
     * 多对多中间操作
     * @param joinAttrMap 关联关系的映射, key=关联对象绑定的标签名, value={left=Affix, middle=JoinAttr, right=LinkAttr}
     * @param targetObjClass 目标对象的Class
     * @param targetObj 目标对象实例
     * @param selectObj 起始对象实例
     * @param relateAffixObjSet  key=关联对象绑定的标签名, value=关联的候选对象集合 [DEL]起始对象.JoinAttr关联的候选关联对象
     */
    protected void bindBatchMultiAttr(
            Map<String, Triple<String,String,String>> joinAttrMap,
            Class<E> targetObjClass,
            Object targetObj,
            Object selectObj,
            Map<String, Collection<?>> relateAffixObjSet){
        // 给tmp赋值聚合属性值
        Set<String> tagSet = joinAttrMap.keySet();
        // 1: 遍历关联对象绑定的tag
        for(String tag : tagSet) {
            Triple<String,String,String> t = joinAttrMap.get(tag);
            // 2: 前缀affix
            final String affix = t.getLeft();
            // 3: 当前要设置的属性集合
            Set<String> setAffixAttrs = getAffixAttrSet(targetObjClass, affix);
            // 4: 从哪个对象中取得要设置的属性, ins中的哪个属性可以作到
            Object relateArgVal = BeanHelper.getAttrVal(selectObj, t.getMiddle());
            Optional<?> relateObjIns = seekCollectionEle(relateArgVal, t.getRight(), relateAffixObjSet.get(tag));
            if (!relateObjIns.isPresent()){
                continue;
            }
            Map<String,Object> tsObjAttrValMap = getGroupObjAttrVal(relateObjIns.get(), affix, setAffixAttrs);
            // 5: 设置对象的指定属性值集合
            setTargetMultiAttrVal(targetObj, tsObjAttrValMap);
        }
    }
}
